package cn.woniu.ecommerceplatform.service.impl;

import cn.woniu.ecommerceplatform.entity.Address;
import cn.woniu.ecommerceplatform.entity.Customer;
import cn.woniu.ecommerceplatform.entity.Orders;
import cn.woniu.ecommerceplatform.mapper.AddressMapper;
import cn.woniu.ecommerceplatform.mapper.CustomerMapper;
import cn.woniu.ecommerceplatform.mapper.OrdersMapper;
import cn.woniu.ecommerceplatform.service.CustomerService;
import cn.woniu.ecommerceplatform.util.ResponseResult;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class CustomerServiceImpl implements CustomerService {
    @Autowired(required = false)
    private CustomerMapper customerMapper;
    @Autowired(required = false)
    private RedisTemplate redisTemplate;
    @Autowired(required = false)
    private OrdersMapper ordersMapper;
    @Autowired(required = false)
    private AddressMapper addressMapper;
    public ResponseResult<?> queryCustomList(String phone, String name, String level, Integer pageNo,Integer pageSize,Integer pageCount, String orderBy) {

        //pageNo起始页数,pageSize每页条数,pageCount一共多少页,pageTotal一共多少条;
        //判断redis是否有数据, 如果有数据, 拿来用; 如果没有, 查询数据库
        //redisOps
        final String key = "customs";
        ZSetOperations ops = redisTemplate.opsForZSet();
        if (ops.size(key)>0){
            //模糊查询, 从redis中获取需要的信息
            //判断, 如果传入的关键词不为空, 开启模糊搜索
            boolean p = phone != null && !phone.equals("") && !phone.equals("null");
            boolean n = name != null && !name .equals("")&& !name .equals("null");
            boolean l = level != null && !level .equals("") && !level .equals("null");
            Integer pageTotal = 0;
            List<Customer> resultList = new ArrayList<Customer>();
            List<Customer> custList = new ArrayList<Customer>();
            if (p || n || l){
                //关键词不为空,模糊查询
                List<Customer> custListTepm = this.queryRedisByKeyWord(phone, name, level, ops);
                //排序
                Set<Customer> customers = this.queryRedis(orderBy, custListTepm, ops);
                //Set<Customer> customers = this.intoRedis(orderBy, custListTepm);
                custList.addAll(customers);
            }else{
                //关键词为空, 不需要使用redis筛选,需要进行排序
                Set range = ops.range(key, 0, -1);
                List<Customer> list = new ArrayList<>(range);
                //排序
                Set<Customer> customers = this.queryRedis(orderBy, list, ops);
                custList.addAll(customers);
            }

            //开始分页
            //判断是否返回值为空
            if (custList.size() != 0) {
                pageTotal = custList.size();
                pageCount = (int) Math.ceil(pageTotal * 1.0 / pageSize);
                //计算起始条数pageIndex
                Integer pageIndex = (pageNo - 1) * pageSize;
                for (int i = pageIndex; i < (pageNo * pageSize); i++) {
                    resultList.add(custList.get(i));
                    if (i == custList.size() - 1) {
                        break;
                    }
                }
            }else{
                pageTotal = 0;
                pageCount = 1;
            }
            return new ResponseResult<>(200,"success!",pageNo,pageSize,pageCount,pageTotal,resultList);
        } else{
            //redis没有数据
            //查询列表
            List<Customer> customers = customerMapper.queryCustomer(phone, name, level);
            //2. 列表补充退换货比例的属性
            customers.forEach(e ->{
                if (e.getCustomNumber() == null){
                    e.setCustomNumber(0D);
                }
                if (e.getCustomMoney() == null){
                    e.setCustomMoney(0d);
                }
                //获取退换货次数
                Customer temp = customerMapper.queryReturnNumberOfTimesByAccount(e.getAccount());
                //判断退换货次数是否为null, 是则设置为0
                try {
                    if (temp.getReNum() != null){
                        e.setReNum(temp.getReNum());
                    }else{
                        e.setReNum(0);
                    }
                } catch (NullPointerException ex) {
                    e.setReNum(0);
                }

                //计算比例x
                BigDecimal reNum = new BigDecimal(e.getReNum().toString());
                BigDecimal cNum = new BigDecimal(e.getCustomNumber().toString());
                Double r = null;
                try {
                    r = reNum.divide(cNum,2, RoundingMode.HALF_UP).doubleValue();
                } catch (Exception ex) {
                    e.setReRatio(0d);
                }
                e.setReRatio(r);
                System.out.println("reNum="+reNum+" | cNum="+cNum+" | r="+r);
            });
            //判断列表是否有数据,如果有,列表数据写入redis并返回排序后的数据
            if (customers.size() > 0) {
                //对列表数据进行处理, 如果消费次数和消费金额为null,则写入0值;
                customers.forEach(e ->{
                    if (e.getCustomNumber() == null){
                        e.setCustomNumber(0D);
                    }
                    if (e.getCustomMoney() == null){
                        e.setCustomMoney(0d);
                    }
                });
                //通过ZSet,按照orderBy的要求排序
                Set<Customer> set = this.intoRedis(orderBy, customers);

                //判断写入redis的结果, 如果返回值不为250,分页,将数据返回;
                if (set.size() >0){
                    //分页,需要List
                    //开始分页
                    List<Customer> custList = new ArrayList<Customer>(set);
                    Integer pageTotal = custList.size();
                    pageCount =(int) Math.ceil(pageTotal *1.0/ pageSize);
                    //计算起始条数pageIndex
                    Integer pageIndex = (pageNo-1)*pageSize;
                    List<Customer> resultList = new ArrayList<Customer>();
                    for (int i = pageIndex; i <(pageCount+pageSize) ; i++) {
                        resultList.add(custList.get(i));
                        if(i == custList.size()-1){
                            break;
                        }
                    }
                    return new ResponseResult<>(200,"success!",pageNo,pageSize,pageCount,pageTotal,resultList);
                }else{
                    //写入redis出错;
                    return new ResponseResult<>(250,"write into redis FAILED!");
                }

            } else {
                //查询失败
                return new ResponseResult<>(250,"check MySQL failed!");
            }
        }

    }


    public ResponseResult<?> insertCustomer(Customer customer) {
        Integer integer = customerMapper.insertCustomer(customer);
        if (integer>0){
            redisTemplate.delete("customs");
            return new ResponseResult<>(200,"新增成功!");
        }else{
            return new ResponseResult<>(250,"新增失败!");
        }
    }


    public ResponseResult<?> updateCustomer(Customer customer) {
        Integer integer = customerMapper.updateCustomer(customer);

        if (integer>0){
            redisTemplate.delete("customs");
            return new ResponseResult<>(200,"修改成功!");
        }else{
            return new ResponseResult<>(250,"修改失败!");
        }
    }


    public ResponseResult<?> deleteCustomer(Integer id) {
        Customer customer = customerMapper.selectCustomerById(id);
        Integer integer = customerMapper.deleteCustomerById(id);
        if (integer>0){
            //删除成功, 更新redis
            redisTemplate.delete("customs");
            return new ResponseResult<>(200,"删除成功!");
        }else{
            return new ResponseResult<>(250,"删除失败!");
        }
    }

    @Override
    public ResponseResult<?> queryBlackList(String phone, String name, Integer pageNo, Integer pageSize) {
        //查询黑名单列表, 模糊查询, 分页使用分页器, 内容不再存储redis
        PageHelper.startPage(pageNo,pageSize);
        //1. 查询黑名单列表
        List<Customer> customers = customerMapper.queryBlackList(phone, name);
        //2. 列表补充退换货比例的属性
        customers.forEach(e ->{
            if (e.getCustomNumber() == null){
                e.setCustomNumber(0D);
            }
            if (e.getCustomMoney() == null){
                e.setCustomMoney(0d);
            }
            //获取退换货次数
            Customer temp = customerMapper.queryReturnNumberOfTimesByAccount(e.getAccount());
            //判断退换货次数是否为null, 是则设置为0
            try {
                if (temp.getReNum() != null){
                    e.setReNum(temp.getReNum());
                }else{
                    e.setReNum(0);
                }
            } catch (NullPointerException ex) {
                e.setReNum(0);
            }

            //计算比例x
            double x = e.getReNum() / e.getCustomNumber();
            double y = Math.round(x*100) * 0.01;
            e.setReRatio(y);
        });

        PageInfo<Customer> listPageInfo = new PageInfo<>(customers);
        return new ResponseResult<>().ok(listPageInfo);

    }

    @Override
    public ResponseResult<?> removeBlackList(String account) {
        Integer integer = customerMapper.removeBlackList(account);
        if (integer>0){
            redisTemplate.delete("customs");
            return new ResponseResult<>(200,"成功移出黑名单!");
        }else{
            return new ResponseResult<>(250,"移出黑名单失败!");
        }
    }

    /**
     * 移入黑名单
     * @param account
     * @return
     */
    @Override
    public ResponseResult<?> insertBlackList(String account) {
        Integer integer = customerMapper.insertBlackList(account);
        if (integer>0){
            redisTemplate.delete("customs");
            return new ResponseResult<>(200,"成功移入黑名单!");
        }else{
            return new ResponseResult<>(250,"移入黑名单失败!");
        }
    }

    @Override
    public ResponseResult<?> selectByAccount(String account) {
        Customer customer = customerMapper.selectByAccount(account);
        try {
            if (customer != null || customer.getId() != null){
                //查询结果不为空
                return new ResponseResult<>().ok(customer);
            }else{
                //空指针异常, 查询结果为空
                return new ResponseResult<>().isNull();
            }
        } catch (Exception e) {
            e.printStackTrace();
            //空指针异常, 查询结果为空
            return new ResponseResult<>().isNull();
        }
    }

    @Override
    @Transactional
    public ResponseResult<?> countPoint(Address address, Orders orders,Integer pointNeed) {
        //查询客户的积分
        Customer customer = customerMapper.selectByAccount(address.getAccount());
        Integer point = customer.getPoint();
        String redis_lock = "REDISLOCK";
        String vaule = UUID.randomUUID().toString().replace("-","");
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(redis_lock,vaule,10, TimeUnit.MINUTES);
                //opsForValue
        if (!flag){
            System.out.println("抢锁失败");
            return new ResponseResult<>().notok("抢锁失败!");
        }
        System.out.println("抢锁成功");
        try {
            if (point >= pointNeed){
                //可以扣减, 会员的积分充足
                Integer integer = customerMapper.countPoint(address.getAccount(), pointNeed);
                if (integer>0){
                    //扣减成功,生成订单
                    int i = ordersMapper.orderAdd(orders);
                    if (i>0){
                        //订单生成成功,返回成功
                        //写入地址:orderID,region,account,address
                        List<Orders> orders1 = ordersMapper.orderList("", orders.getOrderNum(), 2, "");
                        address.setOrderID(orders1.get(0).getId());
                        Integer j = addressMapper.insertAddressForOrder(address);
                        if (j>0){
                            return new ResponseResult<>(200,"积分已扣除, 订单已生成,写入地址成功!");
                        }else {
                            return new ResponseResult<>().notok("积分已扣除,订单已生成,写入地址失败!");
                        }
                    }else{
                        return new ResponseResult<>(250,"积分已扣除,订单生成失败!");
                    }
                }else{
                    //扣减失败, 不生成订单, 返回错误信息
                    return new ResponseResult<>().notok("扣除积分失败!");
                }
            }else{
                //积分不足以兑换, 返回信息
                return new ResponseResult<>().notok("积分不足!");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally { //线程只删除自己生成的uuid
            if ( redisTemplate.opsForValue().get(redis_lock).equals(vaule)){
                redisTemplate.delete(redis_lock);
            }
        }
        return new ResponseResult<>(0,"执行了扣减积分的代码");
    }

    /**
     * 封装了一个根据排序指令,将List<Customer>写入redis的方法.
     * @param orderBy "N"按照会员消费次数排序;"M"按照会员消费总金额排序;其他,默认按照开卡时间排序
     * @param list List<Customer>
     * @return 返回ResponseResult
     */
    public Set<Customer> intoRedis(String orderBy, List<Customer> list) {
        //redisOps
        final String key = "customs";
        ZSetOperations ops = redisTemplate.opsForZSet();
        //Switch语句判断排序,默认值为按照T排队
        try {
            switch (orderBy) {
                case "N": {                    //按照消费次数排序
                    //遍历集合,装入redis
                    list.forEach(e -> {
                        ops.add(key, e, e.getCustomNumber());
                    });
                    //从redis中取出排序后的List
                    Set setN = ops.reverseRange(key, 0, -1);
                    return setN;
                }
                case "M": {
                    //按照消费总金额排序
                    //遍历集合,装入redis
                    list.forEach(e -> {
                        ops.add(key, e, e.getCustomMoney());
                    });
                    //从redis中取出排序后的List
                    Set setM = ops.reverseRange(key, 0, -1);
                    return setM;
                }
                default: {
                    //按照开卡时间排序
                    //遍历集合,装入redis
                    list.forEach(e -> {
                        ops.add(key, e, e.getCreateTime().getTime());
                    });
                    //从redis中取出排序后的List
                    Set setT = ops.range(key, 0, -1);
                    return setT;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Set<Customer> set = new HashSet<>();
            return set;
        }
    }

    /**
     * 封装了一个方法,将redis中的数据取出并按照排序指令排序,重新写入redis并返回一个排序后的set
     * @param orderBy
     * @param ops
     * @return
     */
    public Set<Customer> queryRedis(String orderBy,List<Customer> list,ZSetOperations ops){
        final String key = "customsTemp";

       //Set<Customer> range = ops.range(key, 0, -1);
        switch (orderBy){
            case "N":{
                try {
                    //ZSet按照消费次数排序,重新写入redis并返回set
                    list.forEach(e ->{
                        ops.add(key,e,e.getCustomNumber());
                    });
                    return ops.reverseRange(key,0,-1);
                } finally {
                    redisTemplate.delete(key);
                }
            }
            case "M":{
                //ZSet按照消费总金额排序,重新写入redis并返回set
                try {
                    list.forEach(e ->{
                        ops.add(key,e,e.getCustomMoney());
                    });
                    return ops.reverseRange(key,0,-1);
                } finally {
                    redisTemplate.delete(key);
                }
            }
            default:{
                //ZSet按照开卡时间排序,重新写入redis并返回set
                try {
                    list.forEach(e ->{
                        ops.add(key,e,e.getCreateTime().getTime());
                    });
                    return ops.reverseRange(key,0,-1);
                } finally {
                    redisTemplate.delete(key);
                }
            }
        }
    }

    /**
     * 封装一个从redis中进行模糊查询的方法
     */
    public List<Customer> queryRedisByKeyWord(String phone, String name, String level,ZSetOperations ops){
        final String key = "customs";
        //模糊查询
        Set<Customer> range = ops.range(key, 0, -1);
        List<Customer> list = new ArrayList<>(range);
        List<Customer> resultList = new ArrayList<>();
        //过滤
//         resultList = list.stream()
//                .filter(e ->(e.getPhone().contains(phone) || e.getName().contains(name) || e.getLevel().equals(level)))
//                .collect(Collectors.toList());
        //手动过滤
        if (name == ""){
            name = "*";
        }
        if (phone == ""){
            phone = "*";
        }
        for (Customer e:list) {
            if (e.getName().contains(name)){
                resultList.add(e);
            }else if(e.getPhone().contains(phone)){
                resultList.add(e);
            }else if(e.getLevel().equals(level)){
                resultList.add(e);
            }
        }
        return resultList;
    }


    /**
     * 注册验证account是否唯一
     * @param account
     * @return
     */
    @Override
    public ResponseResult<?> queryAccount(String account) {
        Customer customer = customerMapper.queryAccount(account);
        if(customer == null){
            return new ResponseResult<>().ok();
        }else{
            return new ResponseResult<>().notok();
        }
    }

    /**
     * 注册新增
     * @param
     * @return
     */
    @Override
    public ResponseResult<?> insertRegister(String name,String phone,String account,String password) {
        Customer customer = new Customer();
        customer.setName(name);
        customer.setPhone(phone);
        customer.setAccount(account);
        customer.setPassword(password);
        customer.setCreateTime(new Date());

        return new ResponseResult<>().ok(customerMapper.insertRegister(customer));
    }

    @Override
    public ResponseResult<?> queryIdByAccount(String account) {
        return new ResponseResult<>().ok(customerMapper.queryIdByAccount(account));
    }

    @Override
    public ResponseResult<?> addPoint(String account, Double actualPay) {
        Integer integer = customerMapper.addPoint(account, actualPay);
        if(integer >0 ){
            return new ResponseResult<>().ok();
        }else{
            return new ResponseResult<>().notok();
        }
    }

    /**
     * 会员查询自己的可显示信息
     * @param account
     * @return
     */
    @Override
    public ResponseResult<?> selectMyself(String account) {
        return new ResponseResult<>().ok(customerMapper.selectMyself(account));
    }

    /**
     * 会员通过id修改自己的信息
     * @param customer
     * @return
     */
    @Override
    public ResponseResult<?> updateCustomByAccountMy(Customer customer) {
        return new ResponseResult<>().ok(customerMapper.updateCustomByAccountMy(customer));
    }


}
